// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 动作图创建（计划）。

package work

import (
	"bufio"
	"bytes"
	"container/heap"
	"context"
	"debug/elf"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"cmd/go/internal/base"
	"cmd/go/internal/cache"
	"cmd/go/internal/cfg"
	"cmd/go/internal/load"
	"cmd/go/internal/trace"
	"cmd/internal/buildid"
)

// 一个构建者持有一个构建的全局状态。du jian defg
type Builder struct {
	WorkDir     string               // 临时工作目录（以filepath.Separator结尾）
	actionCache map[cacheKey]*Action // 已构造操作的缓存
	mkdirCache  map[string]bool      // 已创建目录的缓存
	flagCache   map[[2]string]bool   // 支持的编译器标志的缓存
	Print       func(args ...any) (int, error)

	IsCmdList           bool // 作为go列表的一部分运行；设置p.在
	NeedError           bool // 列表需要p.错误
	NeedExport          bool // 列表需要p.导出
	NeedCompiledGoFiles bool // 列表需要p.编译文件

	objdirSeq int // NewObjdir 
	pkgSeq    int

	output    sync.Mutex
	scriptDir string // 打印脚本中的当前目录

	exec      sync.Mutex
	readySema chan bool
	ready     actionQueue

	id           sync.Mutex
	toolIDCache  map[string]string // 工具名->工具ID 
	buildIDCache map[string]string // 文件名->构建ID 
}

// 注意：大部分操作如果不用于测试，则不需要导出。
// 也许测试功能也应该转移到这个包中？

// 一个动作代表动作图中的一个动作。
type Action struct {
	Mode       string                                         // 操作说明
	Package    *load.Package                                  // 此操作适用于
	Deps       []*Action                                      // 此操作之前必须执行的操作
	Func       func(*Builder, context.Context, *Action) error // 操作本身（nil=no op）
	IgnoreFail bool                                           // 即使依赖项失败也是否运行f 
	TestOutput *bytes.Buffer                                  // 测试输出缓冲区
	Args       []string                                       // 运行程序

	triggers []*Action // 与deps 

	buggyInstall bool // 相反，这是一个错误安装（请参阅-linkshared）？

	TryCache func(*Builder, *Action) bool // 缓存绕过回调

	// 生成的文件、目录。
	Objdir   string         // 中间对象目录
	Target   string         // 操作的目标：创建的包或可执行的
	built    string         // 实际创建的包或可执行的
	actionID cache.ActionID // 操作输入的缓存ID 
	buildID  string         // 操作输出的构建ID 

	VetxOnly  bool       // 模式=“vet”：仅被调用以提供有关依赖关系的信息
	needVet   bool       // 模式==“build”：需要填写vet config 
	needBuild bool       // Mode==“build”：需要进行实际构建（如果needVet为true，则可能为false）
	vetCfg    *vetConfig // vet config 
	output    []byte     // 输出重定向缓冲区（nil表示使用b.Print）

	// 执行状态。
	pending      int               // 尚未完成的DEP数
	priority     int               // 相对执行优先级
	Failed       bool              // 操作是否失败
	json         *actionJSON       // 操作图信息
	nonGoOverlay map[string]string // 映射自非-。将源文件转到objdir中复制的文件。如果未使用覆盖，则为零。
	traceSpan    *trace.Span
}

// BuildActionID返回a的生成ID的操作ID部分。
func (a *Action) BuildActionID() string { return actionID(a.buildID) }

// BuildContentID返回a的生成ID的内容ID部分。
func (a *Action) BuildContentID() string { return contentID(a.buildID) }

// BuildID返回a的生成ID。
func (a *Action) BuildID() string { return a.buildID }

// BuiltTarget返回生成的实际文件。这与缓存结果时的目标不同。
func (a *Action) BuiltTarget() string { return a.built }

// 操作队列是操作的优先级队列。
type actionQueue []*Action

// 实现堆。接口
func (q *actionQueue) Len() int           { return len(*q) }
func (q *actionQueue) Swap(i, j int)      { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].priority }
func (q *actionQueue) Push(x any)         { *q = append(*q, x.(*Action)) }
func (q *actionQueue) Pop() any {
	n := len(*q) - 1
	x := (*q)[n]
	*q = (*q)[:n]
	return x
}

func (q *actionQueue) push(a *Action) {
	if a.json != nil {
		a.json.TimeReady = time.Now()
	}
	heap.Push(q, a)
}

func (q *actionQueue) pop() *Action {
	return heap.Pop(q).(*Action)
}

type actionJSON struct {
	ID         int
	Mode       string
	Package    string
	Deps       []int     `json:",omitempty"`
	IgnoreFail bool      `json:",omitempty"`
	Args       []string  `json:",omitempty"`
	Link       bool      `json:",omitempty"`
	Objdir     string    `json:",omitempty"`
	Target     string    `json:",omitempty"`
	Priority   int       `json:",omitempty"`
	Failed     bool      `json:",omitempty"`
	Built      string    `json:",omitempty"`
	VetxOnly   bool      `json:",omitempty"`
	NeedVet    bool      `json:",omitempty"`
	NeedBuild  bool      `json:",omitempty"`
	ActionID   string    `json:",omitempty"`
	BuildID    string    `json:",omitempty"`
	TimeReady  time.Time `json:",omitempty"`
	TimeStart  time.Time `json:",omitempty"`
	TimeDone   time.Time `json:",omitempty"`

	Cmd     []string      // `json:“，省略空”`
	CmdReal time.Duration `json:",omitempty"`
	CmdUser time.Duration `json:",omitempty"`
	CmdSys  time.Duration `json:",omitempty"`
}

// /cacheKey是操作缓存的键。
type cacheKey struct {
	mode string
	p    *load.Package
}

func actionGraphJSON(a *Action) string {
	var workq []*Action
	var inWorkq = make(map[*Action]int)

	add := func(a *Action) {
		if _, ok := inWorkq[a]; ok {
			return
		}
		inWorkq[a] = len(workq)
		workq = append(workq, a)
	}
	add(a)

	for i := 0; i < len(workq); i++ {
		for _, dep := range workq[i].Deps {
			add(dep)
		}
	}

	var list []*actionJSON
	for id, a := range workq {
		if a.json == nil {
			a.json = &actionJSON{
				Mode:       a.Mode,
				ID:         id,
				IgnoreFail: a.IgnoreFail,
				Args:       a.Args,
				Objdir:     a.Objdir,
				Target:     a.Target,
				Failed:     a.Failed,
				Priority:   a.priority,
				Built:      a.built,
				VetxOnly:   a.VetxOnly,
				NeedBuild:  a.needBuild,
				NeedVet:    a.needVet,
			}
			if a.Package != nil {
				// TODO（rsc）：以某种方式将其作为包的唯一密钥。
				a.json.Package = a.Package.ImportPath
			}
			for _, a1 := range a.Deps {
				a.json.Deps = append(a.json.Deps, inWorkq[a1])
			}
		}
		list = append(list, a.json)
	}

	js, err := json.MarshalIndent(list, "", "\t")
	if err != nil {
		fmt.Fprintf(os.Stderr, "go: writing debug action graph: %v\n", err)
		return ""
	}
	return string(js)
}

// BuildMode指定构建模式：
// 我们只是在构建东西还是同时安装结果？
type BuildMode int

const (
	ModeBuild BuildMode = iota
	ModeInstall
	ModeBuggyInstall

	ModeVetOnly = 1 << 8
)

func (b *Builder) Init() {
	b.Print = func(a ...any) (int, error) {
		return fmt.Fprint(os.Stderr, a...)
	}
	b.actionCache = make(map[cacheKey]*Action)
	b.mkdirCache = make(map[string]bool)
	b.toolIDCache = make(map[string]string)
	b.buildIDCache = make(map[string]string)

	if cfg.BuildN {
		b.WorkDir = "$WORK"
	} else {
		tmp, err := os.MkdirTemp(cfg.Getenv("GOTMPDIR"), "go-build")
		if err != nil {
			base.Fatalf("go: creating work dir: %v", err)
		}
		if !filepath.IsAbs(tmp) {
			abs, err := filepath.Abs(tmp)
			if err != nil {
				os.RemoveAll(tmp)
				base.Fatalf("go: creating work dir: %v", err)
			}
			tmp = abs
		}
		b.WorkDir = tmp
		if cfg.BuildX || cfg.BuildWork {
			fmt.Fprintf(os.Stderr, "WORK=%s\n", b.WorkDir)
		}
		if !cfg.BuildWork {
			workdir := b.WorkDir
			base.AtExit(func() {
				start := time.Now()
				for {
					err := os.RemoveAll(workdir)
					if err == nil {
						return
					}

					// 在Windows的某些配置中，包含可执行文件
					// 文件的目录在可执行文件退出后可能会被锁定一段时间（可能是
					// 由于防病毒扫描？）。为了避免用户的临时目录被泄露的
					// 文件填满，退出时可能需要额外延迟一点。（见golang.org/issue/30789。）
					if runtime.GOOS != "windows" || time.Since(start) >= 500*time.Millisecond {
						fmt.Fprintf(os.Stderr, "go: failed to remove work dir: %s\n", err)
						return
					}
					time.Sleep(5 * time.Millisecond)
				}
			})
		}
	}

	if err := CheckGOOSARCHPair(cfg.Goos, cfg.Goarch); err != nil {
		fmt.Fprintf(os.Stderr, "go: %v\n", err)
		base.SetExitStatus(2)
		base.Exit()
	}

	for _, tag := range cfg.BuildContext.BuildTags {
		if strings.Contains(tag, ",") {
			fmt.Fprintf(os.Stderr, "go: -tags space-separated list contains comma\n")
			base.SetExitStatus(2)
			base.Exit()
		}
	}
}

func CheckGOOSARCHPair(goos, goarch string) error {
	if _, ok := cfg.OSArchSupportsCgo[goos+"/"+goarch]; !ok && cfg.BuildContext.Compiler == "gc" {
		return fmt.Errorf("unsupported GOOS/GOARCH pair %s/%s", goos, goarch)
	}
	return nil
}

// NewObjdir返回b.WorkDir下新对象目录的名称。
// 由调用方在适当的时间调用b.Mkdir查询结果。
// 结果以斜杠结尾，因此该目录中的文件名
// 可以通过直接字符串加法来构造。
// 
// NewObjdir一次只能从一个goroutine调用，
// 因此在构造动作图期间调用是安全的，但在执行动作图期间不能调用。
func (b *Builder) NewObjdir() string {
	b.objdirSeq++
	return filepath.Join(b.WorkDir, fmt.Sprintf("b%03d", b.objdirSeq)) + string(filepath.Separator)
}

// readpkglist返回在shlibpath上构建到共享库
// 中的包的列表。对于本机工具链，该列表以换行符分隔的形式存储在
// 一个名为“Go\x00\x00”且类型为1的ELF注释中。对于GCCGO，它是从
//  .去出口部。
func readpkglist(shlibpath string) (pkgs []*load.Package) {
	var stk load.ImportStack
	if cfg.BuildToolchainName == "gccgo" {
		f, _ := elf.Open(shlibpath)
		sect := f.Section(".go_export")
		data, _ := sect.Data()
		scanner := bufio.NewScanner(bytes.NewBuffer(data))
		for scanner.Scan() {
			t := scanner.Text()
			if strings.HasPrefix(t, "pkgpath ") {
				t = strings.TrimPrefix(t, "pkgpath ")
				t = strings.TrimSuffix(t, ";")
				pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd(), nil, &stk, nil, 0))
			}
		}
	} else {
		pkglistbytes, err := buildid.ReadELFNote(shlibpath, "Go\x00\x00", 1)
		if err != nil {
			base.Fatalf("readELFNote failed: %v", err)
		}
		scanner := bufio.NewScanner(bytes.NewBuffer(pkglistbytes))
		for scanner.Scan() {
			t := scanner.Text()
			pkgs = append(pkgs, load.LoadImportWithFlags(t, base.Cwd(), nil, &stk, nil, 0))
		}
	}
	return
}

// cacheAction在缓存中查找{mode，p}，并返回结果操作。
// 如果缓存没有这样的操作，则记录并返回f（）。
// TODO（rsc）：将第二个键从*load更改。包到接口{}，
// 以使linkShared中的缓存不那么尴尬？
func (b *Builder) cacheAction(mode string, p *load.Package, f func() *Action) *Action {
	a := b.actionCache[cacheKey{mode, p}]
	if a == nil {
		a = f()
		b.actionCache[cacheKey{mode, p}] = a
	}
	return a
}

// AutoAction返回p的go build或go install的“right”操作。
func (b *Builder) AutoAction(mode, depMode BuildMode, p *load.Package) *Action {
	if p.Name == "main" {
		return b.LinkAction(mode, depMode, p)
	}
	return b.CompileAction(mode, depMode, p)
}

// CompileAction返回编译并可能安装给定包的操作。生成的操作只用于构建包（归档），而不用于链接可执行文件。
// depMode是构建依赖项时要使用的操作（生成或安装）。
// 要将包main转换为可执行文件，请调用b.Link。
func (b *Builder) CompileAction(mode, depMode BuildMode, p *load.Package) *Action {
	vetOnly := mode&ModeVetOnly != 0
	mode &^= ModeVetOnly

	if mode != ModeBuild && (p.Internal.Local || p.Module != nil) && p.Target == "" {
		// 通过本地路径或使用模块导入。没有永久目标。
		mode = ModeBuild
	}
	if mode != ModeBuild && p.Name == "main" {
		// 我们从未安装过。主程序包的文件。
		mode = ModeBuild
	}

	// 构造包构建操作。
	a := b.cacheAction("build", p, func() *Action {
		a := &Action{
			Mode:    "build",
			Package: p,
			Func:    (*Builder).build,
			Objdir:  b.NewObjdir(),
		}

		if p.Error == nil || !p.Error.IsImportCycle {
			for _, p1 := range p.Internal.Imports {
				a.Deps = append(a.Deps, b.CompileAction(depMode, depMode, p1))
			}
		}

		if p.Standard {
			switch p.ImportPath {
			case "builtin", "unsafe":
				// 假软件包-无需构建。
				a.Mode = "built-in package"
				a.Func = nil
				return a
			}

			// gccgo标准库也是“假的”。
			if cfg.BuildToolchainName == "gccgo" {
				// cgo需要目标名称。
				a.Mode = "gccgo stdlib"
				a.Target = p.Target
				a.Func = nil
				return a
			}
		}

		return a
	})

	// 找到构建操作；缓存项可能已被（*Builder）期间的安装操作替换为
	// 。分期付款。
	buildAction := a
	switch buildAction.Mode {
	case "build", "built-in package", "gccgo stdlib":
		// 好的
	case "build-install":
		buildAction = a.Deps[0]
	default:
		panic("lost build action: " + buildAction.Mode)
	}
	buildAction.needBuild = buildAction.needBuild || !vetOnly

	// 构造安装操作。
	if mode == ModeInstall || mode == ModeBuggyInstall {
		a = b.installAction(a, mode)
	}

	return a
}

// VetAction返回在包p上运行go vet的操作。
// 这取决于编译p的操作。
// 如果调用方可能导致安装p，则由调用方
// 确保安装依赖于（在）vet之后运行。
func (b *Builder) VetAction(mode, depMode BuildMode, p *load.Package) *Action {
	a := b.vetAction(mode, depMode, p)
	a.VetxOnly = false
	return a
}

func (b *Builder) vetAction(mode, depMode BuildMode, p *load.Package) *Action {
	// 构造检查动作。
	a := b.cacheAction("vet", p, func() *Action {
		a1 := b.CompileAction(mode|ModeVetOnly, depMode, p)

		// vet希望能够导入“fmt”。
		var stk load.ImportStack
		stk.Push("vet")
		p1 := load.LoadImportWithFlags("fmt", p.Dir, p, &stk, nil, 0)
		stk.Pop()
		aFmt := b.CompileAction(ModeBuild, depMode, p1)

		var deps []*Action
		if a1.buggyInstall {
			// /（*建筑商）。兽医希望deps[0]是
			// 包，deps[1]是“fmt”。如果我们在这里看到Buggy安装
			// 那么a1是一个共享库的安装，
			// 真正的包是a1。Deps[0]。
			deps = []*Action{a1.Deps[0], aFmt, a1}
		} else {
			deps = []*Action{a1, aFmt}
		}
		for _, p1 := range p.Internal.Imports {
			deps = append(deps, b.vetAction(mode, depMode, p1))
		}

		a := &Action{
			Mode:       "vet",
			Package:    p,
			Deps:       deps,
			Objdir:     a1.Objdir,
			VetxOnly:   true,
			IgnoreFail: true, // 如果依赖项检查“失败”（报告问题）也可以。
		}
		if a1.Func == nil {
			// 内置包，如不安全。
			return a
		}
		deps[0].needVet = true
		a.Func = (*Builder).vet
		return a
	})
	return a
}

// LinkAction返回将p链接到可执行文件
// 并可能安装结果（根据模式）的操作。
// depMode是编译依赖项时要使用的操作（生成或安装）。
func (b *Builder) LinkAction(mode, depMode BuildMode, p *load.Package) *Action {
	// 构造链接操作。
	a := b.cacheAction("link", p, func() *Action {
		a := &Action{
			Mode:    "link",
			Package: p,
		}

		a1 := b.CompileAction(ModeBuild, depMode, p)
		a.Func = (*Builder).link
		a.Deps = []*Action{a1}
		a.Objdir = a1.Objdir

		// 一个可执行文件。（这是临时文件的名称。）
		// 因为我们在'go run'和'go test'中运行临时文件，
		// 名称将显示在ps清单中。如果调用方指定了
		// 名称，请使用该名称而不是.out。二进制文件是在一个名为exe的空子目录中生成的，以避免命名冲突。唯一可能的冲突是如果我们要创建一个名为exe的顶级包。
		name := "a.out"
		if p.Internal.ExeName != "" {
			name = p.Internal.ExeName
		} else if (cfg.Goos == "darwin" || cfg.Goos == "windows") && cfg.BuildBuildmode == "c-shared" && p.Target != "" {
			// 在OS X上，链接器输出名称记录在共享库的LC_ID_DYLIB load命令中。
			// 调用链接器的代码只知道传递最后的
			// path元素。安排path元素与
			// 匹配，我们将安装为；否则，库只能作为“a.out”加载。
			// 在Windows上，DLL文件名记录在PE文件
			// 导出部分，在OS X上也一样。
			_, name = filepath.Split(p.Target)
		}
		a.Target = a.Objdir + filepath.Join("exe", name) + cfg.ExeSuffix
		a.built = a.Target
		b.addTransitiveLinkDeps(a, a1, "")

		// 在链接中的所有其他依赖项的生成
		// 之后，严格对主包（a1）的生成进行排序。很可能是在
		// /他们之后，但要确保。这是（*Builder）中基于构建ID的
		// 快捷方式所必需的。useCache（a1），它将调用b.linkActionID（a）。
		// 为了让linkActionID调用计算正确的操作ID，a（a1除外）的所有
		// 依赖项必须已完成构建，并已
		// 记录了它们的构建ID。
		a1.Deps = append(a1.Deps, &Action{Mode: "nop", Deps: a.Deps[1:]})
		return a
	})

	if mode == ModeInstall || mode == ModeBuggyInstall {
		a = b.installAction(a, mode)
	}

	return a
}

// installAction返回安装a1结果的操作。
func (b *Builder) installAction(a1 *Action, mode BuildMode) *Action {
	// 因为我们用下面的安装操作覆盖了构建操作，所以
	// a1可能已经是从“build”缓存键获取的安装操作，
	// 调用方根本没有意识到。恭喜你！buggy安装现在是正确的安装。
	if strings.HasSuffix(a1.Mode, "-install") {
		if a1.buggyInstall && mode == ModeInstall {
			a1.buggyInstall = false
		}
		return a1
	}

	// 如果没有实际行动来构建a1，
	// 也没有什么可安装的。
	// 如果a1对应于重用已构建的对象，则会发生这种情况。
	if a1.Func == nil {
		return a1
	}

	p := a1.Package
	return b.cacheAction(a1.Mode+"-install", p, func() *Action {
		// 安装将删除临时生成结果
		// 因此，我们需要所有其他操作，包括过去和未来的操作，
		// 这些操作试图依赖于构建，而不是安装。

		// 制作a1（构建操作）的私有副本，
		// 任何其他规则都无法访问。
		buildAction := new(Action)
		*buildAction = *a1

		// 用安装操作覆盖a1。
		// 这负责更新
		// 针对构建操作指向a1的过去操作；现在他们将
		// 指向a1并获得安装操作。
		// 我们还将a1保留在操作缓存中，作为“build”的结果
		// 这样，尚未创建的操作
		// 尝试依赖于构建将依赖于安装。
		*a1 = Action{
			Mode:    buildAction.Mode + "-install",
			Func:    BuildInstallFunc,
			Package: p,
			Objdir:  buildAction.Objdir,
			Deps:    []*Action{buildAction},
			Target:  p.Target,
			built:   p.Target,

			buggyInstall: mode == ModeBuggyInstall,
		}

		b.addInstallHeaderAction(a1)
		return a1
	})
}

// addTransitiveLinkDeps将a1的可传递依赖项的所有包
// 添加到链接操作中。副局长。
// 也就是说，如果a是包main的链接，a1是包main的编译
// 和a1。Deps是由
// package main（编译器所需）直接导入的构建包的操作。链接器需要整个程序传递导入的所有包
// ；addTransitiveLinkDeps 
// 确保这些都存在于a.Deps中。
// 如果shlib为非空，则a对应于shlib的生成和安装，
// 因此不应将shlib的任何重建添加为依赖项。
func (b *Builder) addTransitiveLinkDeps(a, a1 *Action, shlib string) {
	// 扩展Deps以包括链接器的所有构建包。
	// 使用广度优先搜索，在标准测试包之前查找重新生成的测试包
	// 。
	// TODO（rsc）：从动作图中删除标准动作，
	// 这将需要进行更多的重建。
	workq := []*Action{a1}
	haveDep := map[string]bool{}
	if a1.Package != nil {
		haveDep[a1.Package.ImportPath] = true
	}
	for i := 0; i < len(workq); i++ {
		a1 := workq[i]
		for _, a2 := range a1.Deps {
			// TODO（rsc）：一旦尘埃落定，找到一个比模式字符串更好的鉴别器。
			if a2.Package == nil || (a2.Mode != "build-install" && a2.Mode != "build") || haveDep[a2.Package.ImportPath] {
				continue
			}
			haveDep[a2.Package.ImportPath] = true
			a.Deps = append(a.Deps, a2)
			if a2.Mode == "build-install" {
				a2 = a2.Deps[0] // “build”操作的walk children 
			}
			workq = append(workq, a2)
		}
	}

	// 如果这是go build-linkshared，那么除了包本身，链接还依赖于共享库
	// 。（编译步骤没有。）
	if cfg.BuildLinkshared {
		haveShlib := map[string]bool{shlib: true}
		for _, a1 := range a.Deps {
			p1 := a1.Package
			if p1 == nil || p1.Shlib == "" || haveShlib[filepath.Base(p1.Shlib)] {
				continue
			}
			haveShlib[filepath.Base(p1.Shlib)] = true
			// TODO（rsc）：在这里使用ModeInstall是可疑的，但是如果我们只使用ModeBuild，
			// 我们最终将构建一个整体库或可执行文件，在运行时依赖于其他过时的库，这显然也不好。
			// 我们称之为ModeBuggyInstall是为了明确这是不对的。
			a.Deps = append(a.Deps, b.linkSharedAction(ModeBuggyInstall, ModeBuggyInstall, p1.Shlib, nil))
		}
	}
}

// 如果需要，addInstallHeaderAction会将安装头操作添加到。
// 操作a应该是由模式为ModeInstall的
// b.CompileAction或b.LinkAction生成的安装操作，
// 因此a.Deps[0]是相应的生成操作。
func (b *Builder) addInstallHeaderAction(a *Action) {
	// 在c-archive和c-shared模式下为cgo安装头。
	p := a.Package
	if p.UsesCgo() && (cfg.BuildBuildmode == "c-archive" || cfg.BuildBuildmode == "c-shared") {
		hdrTarget := a.Target[:len(a.Target)-len(filepath.Ext(a.Target))] + ".h"
		if cfg.BuildContext.Compiler == "gccgo" && cfg.BuildO == "" {
			// 对于头文件，删除go/build添加的“lib”
			// 因此我们生成pkg。h 
			// 而不是libpkg。h、 
			dir, file := filepath.Split(hdrTarget)
			file = strings.TrimPrefix(file, "lib")
			hdrTarget = filepath.Join(dir, file)
		}
		ah := &Action{
			Mode:    "install header",
			Package: a.Package,
			Deps:    []*Action{a.Deps[0]},
			Func:    (*Builder).installHeader,
			Objdir:  a.Deps[0].Objdir,
			Target:  hdrTarget,
		}
		a.Deps = append(a.Deps, ah)
	}
}

// buildmodeShared将“go build”操作a1引入a1共享库的构建中。副局长。
// 也就是说，输入a1表示“go build pkgs”，结果表示“go build-buildmode=shared pkgs”。
func (b *Builder) buildmodeShared(mode, depMode BuildMode, args []string, pkgs []*load.Package, a1 *Action) *Action {
	name, err := libname(args, pkgs)
	if err != nil {
		base.Fatalf("%v", err)
	}
	return b.linkSharedAction(mode, depMode, name, a1)
}

// linkSharedAction执行与生成包列表相对应的分组操作a1，并返回一个操作，将它们链接到名为shlib的共享库中。
// 如果a1为零，shlib应该是现有共享库的绝对路径，
// 然后linkSharedAction读取该库以查找包列表。
func (b *Builder) linkSharedAction(mode, depMode BuildMode, shlib string, a1 *Action) *Action {
	fullShlib := shlib
	shlib = filepath.Base(shlib)
	a := b.cacheAction("build-shlib "+shlib, nil, func() *Action {
		if a1 == nil {
			// TODO（rsc）：需要找到其他地方来存储配置，
			// 不在pkg目录中。见戈朗。org/issue/22196。
			pkgs := readpkglist(fullShlib)
			a1 = &Action{
				Mode: "shlib packages",
			}
			for _, p := range pkgs {
				a1.Deps = append(a1.Deps, b.CompileAction(mode, depMode, p))
			}
		}

		// 装ldflags的假包裹。
		// 像往常一样，共享库是一个混乱、违反抽象的特例：
		// 我们允许它们使用为命令行参数指定的标志。
		p := &load.Package{}
		p.Internal.CmdlinePkg = true
		p.Internal.Ldflags = load.BuildLdflags.For(p)
		p.Internal.Gccgoflags = load.BuildGccgoflags.For(p)

		// 将隐式依赖项添加到pkgs列表中。
		// Current buildmode=shared强制外部链接模式，
		// 外部链接模式强制导入runtime/cgo（以及
		// arm上的数学）。因此，如果它没有在命令行和
		// 上传递，那么它不存在于另一个共享库中，请将其添加到此处。
		// TODO（rsc）：可能只有当“runtime”在原始包集中时，才会发生这种情况。
		// TODO（rsc）：这可能应该更改为使用load。Linkedeps（p）。
		// TODO（rsc）：我们不为gccgo添加标准库导入
		// ，因为它们总是以任何方式链接。
		// 可能会加载。应使用并更新Linkedeps。假设pkg（运行时/cgo或数学）
		a := &Action{
			Mode:    "go build -buildmode=shared",
			Package: p,
			Objdir:  b.NewObjdir(),
			Func:    (*Builder).linkShared,
			Deps:    []*Action{a1},
		}
		a.Target = filepath.Join(a.Objdir, shlib)
		if cfg.BuildToolchainName != "gccgo" {
			add := func(a1 *Action, pkg string, force bool) {
				for _, a2 := range a1.Deps {
					if a2.Package != nil && a2.Package.ImportPath == pkg {
						return
					}
				}
				var stk load.ImportStack
				p := load.LoadImportWithFlags(pkg, base.Cwd(), nil, &stk, nil, 0)
				if p.Error != nil {
					base.Fatalf("load %s: %v", pkg, p.Error)
				}
				// 已经在另一个共享库
				// 中记账，那么该共享库也包含运行时
				// 所以我们所做的任何事情都将依赖于该库
				// 所以我们不需要在共享库中包含pkg。
				if force || p.Shlib == "" || filepath.Base(p.Shlib) == pkg {
					a1.Deps = append(a1.Deps, b.CompileAction(depMode, depMode, p))
				}
			}
			add(a1, "runtime/cgo", false)
			if cfg.Goarch == "arm" {
				add(a1, "math", false)
			}

			// 链接器步骤仍然需要所有常用的链接器dep。
			// （例如，链接器总是打开runtime.a.）
			for _, dep := range load.LinkerDeps(nil) {
				add(a, dep, true)
			}
		}
		b.addTransitiveLinkDeps(a, a1, shlib)
		return a
	})

	// /安装结果。
	if (mode == ModeInstall || mode == ModeBuggyInstall) && a.Func != nil {
		buildAction := a

		a = b.cacheAction("install-shlib "+shlib, nil, func() *Action {
			// 确定最终安装目标。
			// 安装目标是root/pkg/shlib，其中root是源根
			// 所有软件包都位于其中。
			// TODO（rsc）：也许这个交叉根检查应该应用于完整的
			// 可传递包依赖项列表，而不仅仅是命令行上名为
			// 的列表？
			pkgDir := a1.Deps[0].Package.Internal.Build.PkgTargetRoot
			for _, a2 := range a1.Deps {
				if dir := a2.Package.Internal.Build.PkgTargetRoot; dir != pkgDir {
					base.Fatalf("installing shared library: cannot use packages %s and %s from different roots %s and %s",
						a1.Deps[0].Package.ImportPath,
						a2.Package.ImportPath,
						pkgDir,
						dir)
				}
			}
			// TODO（rsc）：在这里找出并解释gccgo的不同之处。
			if cfg.BuildToolchainName == "gccgo" {
				pkgDir = filepath.Join(pkgDir, "shlibs")
			}
			target := filepath.Join(pkgDir, shlib)

			a := &Action{
				Mode:   "go install -buildmode=shared",
				Objdir: buildAction.Objdir,
				Func:   BuildInstallFunc,
				Deps:   []*Action{buildAction},
				Target: target,
			}
			for _, a2 := range buildAction.Deps[0].Deps {
				p := a2.Package
				if p.Target == "" {
					continue
				}
				a.Deps = append(a.Deps, &Action{
					Mode:    "shlibname",
					Package: p,
					Func:    (*Builder).installShlibname,
					Target:  strings.TrimSuffix(p.Target, ".a") + ".shlibname",
					Deps:    []*Action{a.Deps[0]},
				})
			}
			return a
		})
	}

	return a
}
